今天,我深刻理解了 syncRef 的精髓,這是一種不斷追求平衡與和諧的力量,正如我在努力找回我們之間的平衡一樣。這條學習之路並不容易,但越是困難,我越能感受到成長的力量。程世社季子,等我吧,我會讓我們的心靈再次同步!
#挽回愛情的第八天
這次筆者把重點放在原始碼的解析,讓各位更進一步了解 VueUse
Direction
型別type Direction = 'ltr' | 'rtl' | 'both'
這裡定義了三種方向型別:
'ltr'
(left-to-right):從左到右同步資料。'rtl'
(right-to-left):從右到左同步資料。'both'
:雙向同步資料。這些型別定義是用來進行集合操作的,用以檢查兩個型別(集合)之間的關係,這會在後面影響 TransformType
的判斷:
type Equal<A, B> = [A] extends [B] ? ([B] extends [A] ? true : false) : false
type IntersectButNotEqual<A, B> = Equal<A, B> extends true ? false : A & B extends never ? false : true
type IncludeButNotEqual<A, B> = Equal<A, B> extends true ? false : A extends B ? true : false
type NotIntersect<A, B> = Equal<A, B> extends true ? false : A & B extends never ? true : false
這些定義的邏輯是:
Equal<A, B>
判斷型別 A
是否等於型別 B
。IntersectButNotEqual<A, B>
判斷 A
和 B
是否有交集但不完全相等。IncludeButNotEqual<A, B>
判斷 A
是否為 B
的子集,但不完全相等。NotIntersect<A, B>
判斷 A
和 B
是否沒有交集。可以到以下範例去看看,直接看範例會更有感覺
interface Transform<L, R> {
ltr: (left: L) => R
rtl: (right: R) => L
}
Transform
是一個介面,定義了 ltr
和 rtl
兩個轉換函式:
ltr
:將左邊的值轉換成右邊的值。rtl
:將右邊的值轉換成左邊的值。SyncRefOptions
型別export type SyncRefOptions<L, R, D extends Direction> = ConfigurableFlushSync & {
deep?: boolean
immediate?: boolean
direction?: D
} & TransformType<D, L, R>
SyncRefOptions
定義了用來控制資料同步的選項:
deep
:是否深度監控,當資料是物件或陣列時,會監控其內部的變化。immediate
:是否立即執行監控器。direction
:資料同步的方向,預設是 'both'
。TransformType
:使用之前定義的型別來決定如何轉換資料。syncRef
函式export function syncRef<L, R, D extends Direction = 'both'>(
left: Ref<L>,
right: Ref<R>,
...[options]: Equal<L, R> extends true
? [options?: SyncRefOptions<L, R, D>]
: [options: SyncRefOptions<L, R, D>]
) {
const {
flush = 'sync',
deep = false,
immediate = true,
direction = 'both',
transform = {},
} = options || {}
const watchers: WatchPausableReturn[] = []
const transformLTR = ('ltr' in transform && transform.ltr) || (v => v)
const transformRTL = ('rtl' in transform && transform.rtl) || (v => v)
這段是 syncRef
函式,它會根據選項設定不同的資料同步行為:
left
和 right
,分別是 Ref<L>
和 Ref<R>
,代表要同步的兩個資料。options
是一個同步的選項,用來決定同步的方向、轉換函式等。flush
設為 sync
(同步刷新),deep
為 false
(不深度監控),immediate
為 true
(立即執行),direction
為 both
(雙向同步)。接著,定義了轉換函式:
transformLTR
:左到右的轉換,默認為一個直接回傳值的函式 (v => v)
。transformRTL
:右到左的轉換,默認同樣是一個直接回傳值的函式。 ...[options]: Equal<L, R> extends true
? [options?: SyncRefOptions<L, R, D>]
: [options: SyncRefOptions<L, R, D>]
這段程式碼乍看之下很可怕,這是一個條件類型,根據 L
和 R
是否相等來決定 options
參數是否為可選:
L
和 R
相等 (Equal<L, R> extends true
),則 options
是可選的L
和 R
不相等,則 options
是必需的使用 ...[]
語法是為了使 options
成為一個剩餘參數,允許函式調用時省略這個參數
這個類型定義的巧妙之處在於:
L
和 R
類型相同時省略 options
參數,因為此時可能不需要任何轉換函式。L
和 R
類型不同時,強制要求提供 options
,因為這種情況下可能需要轉換函式來處理類型差異。Direction
類型參數 D
允許在類型層面控制同步的方向。SyncRefOptions<L, R, D>
類型可能包含了根據 L
、R
和 D
定制的選項,以處理不同類型和方向的同步需求。 if (direction === 'both' || direction === 'ltr') {
watchers.push(pausableWatch(
left,
(newValue) => {
watchers.forEach(w => w.pause())
right.value = transformLTR(newValue) as R
watchers.forEach(w => w.resume())
},
{ flush, deep, immediate },
))
}
if (direction === 'both' || direction === 'rtl') {
watchers.push(pausableWatch(
right,
(newValue) => {
watchers.forEach(w => w.pause())
left.value = transformRTL(newValue) as L
watchers.forEach(w => w.resume())
},
{ flush, deep, immediate },
))
}
這段程式碼根據 direction
選項設定不同方向的資料同步邏輯:
direction
是 'both'
或 'ltr'
,它會監聽左邊的資料變化,然後更新右邊的資料。direction
是 'both'
或 'rtl'
,它會監聽右邊的資料變化,然後更新左邊的資料。 const stop = () => {
watchers.forEach(w => w.stop())
}
return stop
}
最後,stop
函式可以停止所有的監控器。這在某些情況下很有用,比如當你不再需要資料同步時,可以使用這個函式來停止監控。
原始碼除了實現雙向資料同步,還會根據不同的情況提供了多種型別檢查和選項來控制同步的行為。它允許:
功能不難,但要實現這個功能需要 typescript 型別體操、 js 基本語法與vue 的技巧,算是非常適合讓各位開發者提升自己的一個小範例,總歸一句話:vueuse 我大哥啦